package com.berchen.Stream;

import com.berchen.Lambda表达式.Employee;
import org.junit.Test;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Stream到底是什么呢？
 *  Stream是数据渠道，用于操作数据源（集合、数组）所生成的元素序列。
 *  集合讲的是数据，Stream讲的是计算。
 *
 *  特点：
 *      1、流自己不会存储元素。
 *      2、流不会改变源对象。相反，流会返回一个持有结果的新流。
 *      3、流操作是延迟执行的，这说明他们会等到需要结果(就是终止操作的时候才会执行)的时候才执行。
 *
 * 操作：
 *  1、创建流。
 *      通过Collection系列集合提供的stream()方法(获取的是串行流)或parallelStream()方法(获取的是并行流，并行流就是多个线程同时操作流)。
 *      通过Arrays中的静态方法stream()获取数组流。
 *      通过Stream类中的静态方法of()获取流。
 *      创建无限流。
 *          1、迭代
 *          2、生成
 *  2、中间操作。
 *      filter(Predicate)：筛选，接收Lambda。从流中排除一些元素.Predicate中的boolean test(T);
 *      limit(Integer)：截断流，使其元素不超过给定数量。
 *      skip(Integer)：跳过元素，使流扔掉前n个元素，如果流中不足n个元素，则返回一个空流。与limit方法互补。
 *      distinct()：筛选，通过流生成元素的hashCode()和equals()去掉重复元素。
 *      map(Function)：接收Lambda，将元素转换成其他形式或提取信息。接收一个函数作为参数，该函数会被应用到每个元素上，并将其映射成一个新的元素。
 *          Function中的 R apply(T)，方法将一个类型转成另一个类型。
 *      flatMap()：接收一个函数作为参数，将流中的每一个值都换成另一个流，然后把所有流连接成一个流。
 *          map和flatMap方法就想Collection集合中的add和addAll方法一样。如果add和addAll方法的参数都是一个集合，那么add将集合存储在集合中，addAll是将集合中的元素添加到集合中。
 *      sorted()：自然排序。Comparable接口。
 *      sorted(Comparator com)：定制排序规则。
 *
 *
 *  3、终止操作（终端操作）。
 *      forEach(Consumer)：遍历流中元素。Consumer中的accept(T)。
 *      allMatch(Predicate)：检查是否匹配所有元素。 boolean test(T)
 *      anyMatch(Predicate)：检查是否至少匹配一个元素。
 *      noneMatch(Predicate)：检查是否没有匹配的元素。元素中没有这个元素，返回true，如果有返回false。
 *      findFirst()：返回第一个元素。
 *      findAny()：返回当前流中的任意元素。
 *      count()：返回流中元素的总个数。
 *      max(Comparator)：返回流中最大值。
 *      min(Comparator)：返回流中最小值。
 *      reduce(T identity,BinaryOperator) / reduce(BinaryOperator)： 将流中元素反复结合起来，得到一个值。
 *      collect(Collector/Supplier)：将流转换为其他形式，接收一个Collector接口的实现，用于给Stream中元素做火鬃的方法。
 *
 *
 *  多个中间操作可以连接起来形成一个流水线，除非流水线上触发终止操作，否则中间操作不会执行任何的处理。而在终止操作时一次性全部处理，称为惰性求值。
 *  内部迭代：迭代操作由Stream完成的。
 *  外部迭代：迭代操作由自己写。list.iterator()。获取迭代器。hasNext()判断是否还有元素。next()获取元素并且移动指针。
 */
public class TestStream {

    List<Employee> emps=Arrays.asList(
            new Employee("chen",12,22, Employee.Status.FREE),
            new Employee("chen",12,22, Employee.Status.BUSY),
            new Employee("chen",12,22, Employee.Status.VOCATION),
            new Employee("chenwan",13,24, Employee.Status.FREE),
            new Employee("chenhao",14,25, Employee.Status.BUSY),
            new Employee("chenwanhao",15,12, Employee.Status.VOCATION)
            );

    // 终止操作
    @Test
    public void test3(){

        // allMatch(Predicate) 检查流中是否是【所有元素】都匹配Predicate的test中的东西
        boolean b = emps.stream().allMatch(e -> e.getStatus().equals(Employee.Status.BUSY));
        System.out.println(b);  // false

        // anyMatch(Predicate) 是否至少匹配一个元素
        boolean b1 = emps.stream().anyMatch(e -> e.getStatus().equals(Employee.Status.BUSY));
        System.out.println(b1); // true

        // noneMatch(Predicate) 检查是否有匹配的元素
        boolean b2 = emps.stream().noneMatch(e -> e.getStatus().equals(Employee.Status.BUSY));
        System.out.println(b2); // false

        // findFirst() 获取第一个元素 Optional是一个容器
        Optional<Employee> first = emps.stream().sorted((e1, e2) -> Integer.compare(e1.getAge(), e2.getAge())).findFirst();
        System.out.println(first.toString());

        // findAny() 返回流中任意一个元素
        Optional<Employee> any = emps.stream().filter(e->e.getStatus().equals(Employee.Status.FREE)).findAny();
        System.out.println(any);

        // count() 返回流中元素总数
        long count = emps.stream().count();
        System.out.println(count);

        // max() 返回流中最大值
        Optional<Employee> max = emps.stream().max((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
        System.out.println(max);

        // min() 返回流中最小值
        Optional<Double> min = emps.stream().map(Employee::getSalary).min(Double::compare);
        System.out.println("最小工资"+min.get());

        // reduce 将流中元素反复结合起来，获取一个值
        // 上面这个返回的是Integer，因为reduce方法中给了一个起始值，就不可能为空，
        // 下面那个返回的是Optional，因为reduce方法中没有给起始值，可能为空。
        Integer sum = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9).reduce(0, (x, y) -> x + y);  // 0 作为起始值，0+流中第一个元素，结果+流中第二个元素。。。依次计算。
        System.out.println(sum);
        // map和reduce的连接通常称为map-reduce模式，因为Google用它进行网络搜索而出名。
        Optional<Double> reduce = emps.stream().map(Employee::getSalary).reduce((x, y) -> x + y);
        System.out.println("工资总和"+reduce.get());

        // collect 将流转换成其他形式,取出流中名字并且将名字存在集合中。
        List<String> listName = emps.stream().map(Employee::getName).collect(Collectors.toList());
        listName.forEach(System.out::println);
        // 放到这些集合中
        HashSet<String> collect = emps.stream().map(Employee::getName).collect(Collectors.toCollection(HashSet::new));
        emps.stream().collect(Collectors.counting());   // 总个数
        emps.stream().collect(Collectors.averagingInt(Employee::getAge));   // 平均值
        emps.stream().collect(Collectors.summingInt(Employee::getAge)); // 年龄总和
        emps.stream().collect(Collectors.maxBy((x,y)->Integer.compare(x.getAge(),y.getAge()))); // 求出年龄最大值
        emps.stream().collect(Collectors.minBy((x,y)->Double.compare(x.getSalary(),y.getSalary())));    // 工资最小值
        Map<Employee.Status, List<Employee>> collect1 = emps.stream().collect(Collectors.groupingBy(Employee::getStatus));  // 按照Status分组
        Map<Employee.Status,
                Map<Integer,
                        Map<String, List<Employee>>>> collect2 = emps.stream().collect(
                Collectors.groupingBy(Employee::getStatus,
                        Collectors.groupingBy(Employee::getAge,
                                Collectors.groupingBy(Employee::getName)
                        )));// 多级分组
        Map<Boolean, List<Employee>> collect3 = emps.stream().collect(Collectors.partitioningBy(s -> s.getSalary() > 4000));// 分区，分为true和false区，满足条件一个区，不满足一个区
        DoubleSummaryStatistics collect4 = emps.stream().collect(Collectors.summarizingDouble(Employee::getSalary)); // 获取结果集，直接获取下面的
            System.out.println(collect4.getMax());
            System.out.println(collect4.getSum());
            System.out.println(collect4.getAverage());
            System.out.println(collect4.getCount());
        String str = emps.stream().map(Employee::getName).collect(Collectors.joining(",")); // 逗号不写也可以    // 将流中元素连接起来


    }

    // 中间操作
    @Test
    public void test2(){

        // filter 根据条件筛选
        Stream<Employee> stream = emps.stream();    // 获取流
//        stream.filter(x->x.getAge()>13).forEach(System.out::println);    // 筛选age>13的Employee
        stream.filter(x->{
            System.out.println("filter是中间操作，这条语句没有终止操作，所以这条不会执行");
            return x.getAge()>13;
        });    // 没有终止操作的流不会执行。

        // limit    只保留n个元素
        Stream<Employee> stream1 = emps.stream();    // 获取流
        stream1.limit(2).forEach(System.out::println);   // 只保留2个流中的元素

        // skip     跳过n个元素
        Stream<Employee> stream2 = emps.stream();    // 获取流
        stream2.skip(2).forEach(System.out::println);   // 跳过前2个元素

        // distinct     去重
        Stream<Employee> stream3 = emps.stream();    // 获取流
        stream3.distinct().forEach(System.out::println);    // 去除重复元素。但是元素必须得重写hashCode和equals方法。如果没有重写该两方法那么该方法也就没作用。

        // map   将一个元素映射成另外一个元素。
        List<String> list=Arrays.asList("aaa","bbb","ccc","ddd");
        Stream<String> stream4 = list.stream();
        stream4.map(str->str.toUpperCase()).forEach(System.out::println);   // 将小写的str映射成大写的str

        Stream<Employee> stream5 = emps.stream();    // 获取流
        stream5.map(Employee::getAge).forEach(System.out::println); // 传递一个Employee类型，然后返回一个Integer类型

        // flatMap
        Stream<String> stream6 = list.stream();
        Stream<Stream<Character>> streamStream = stream6.map(TestStream::filterCharacter);  // 流中存的流，里面的流中存的是Character
        streamStream.forEach(s->s.forEach(System.out::println));    // 遍历流，遍历中遍历流。

        Stream<String> stream7 = list.stream();
        Stream<Character> characterStream = stream7.flatMap(TestStream::filterCharacter);   // 通过FlatMap将产生的流与现在的流合并
        characterStream.forEach(System.out::println);

        // sorted()自然排序
        List<String> list1=Arrays.asList("vvv","bbb","ccc","ddd","sds","avc");
        list1.stream().sorted().forEach(System.out::println);

        // sorted(Comparator) 定制排序
        List<String> list2=Arrays.asList("vvv","bbba","cccddd","dddc","sd","avc");
        list2.stream().sorted((e1,e2)->Integer.compare(e1.length(),e2.length())).forEach(System.out::println);  // 按照字符串的长度进行排序
    }
    // 将字符串弄成字符并且弄成流
    public static Stream<Character> filterCharacter(String str){

        // Character是char的包装类。
        List<Character> list=new ArrayList<>();

        for(Character ch:str.toCharArray()){
            list.add(ch);
        }
        return list.stream();
    }


    // 获取流
    @Test
    public void test1(){

        List<Integer> list=new ArrayList<>();
        Stream<Integer> stream = list.stream();// 获取串行流。

        Stream<Integer> integerStream = list.parallelStream();  // 获取并行流

        Employee[] emps=new Employee[10];
        Stream<Employee> stream2 = Arrays.stream(emps);// 获取数组流


        Stream<Integer> integerStream1 = Stream.of(1, 2, 3, 4); // 获取流

        // 0 就是种子，x从0开始。
        Stream<Integer> iterate = Stream.iterate(0, x -> x + 2);    // 获取无限流（迭代）
        iterate.limit(10).forEach(System.out::println); // 只产生10个

        Stream<Double> generate = Stream.generate(() -> Math.random()); // 获取无限流（生成）
        generate.limit(5).forEach(System.out::println);
    }
}
